/*
 * Class representing a media of a library
 * @author M.T. Segarra
 * @version 0.0.1
 */
package inf.mediasManagement;

import inf.hibernateUtils.HibernateUtil;
import inf.exceptions.*;
import inf.loansManagement.Booking;
import inf.loansManagement.Loan;
import inf.specimensManagement.Specimen;

import java.util.*;


import javax.persistence.*;

import org.hibernate.Session;

/**
 * Entity implementation class for Entity: Media
 */
@Entity
@Table(name="medias")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "media_type", discriminatorType = DiscriminatorType.STRING )
public abstract class Media {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name="id", unique = true, nullable = false)
	protected long id;

	protected String title;

	protected String isbnOrIsan;
	
	protected Calendar editionDate;

	@OneToMany(mappedBy = "media", cascade = CascadeType.ALL)
	protected List<Author> authors;

	@OneToMany(mappedBy = "media", cascade = CascadeType.ALL)
	protected List<Specimen> specimens;

	// Reservations en attentes
	@OneToMany(mappedBy = "media", cascade = CascadeType.ALL)
	protected List<Booking> bookings;

	// Nombre d'exemplaires empruntes
	protected int lentSpecimens;

	// Nombre d'exemplaires reserves
	protected int bookedSpecimens;
	
	// Nombre d'exemplaires total
	protected int nbCreatedSpecimen;

	// Type de media, Dvd Ou Book
	protected String type;


	/** Methods **/

	/** Accesseurs **/
	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getIsbnOrIsan() {
		return isbnOrIsan;
	}

	public void setIsbnOrIsan(String isbnOrIsan) {
		this.isbnOrIsan = isbnOrIsan;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public int getNbAvailableSpecimen() {
		return nbCreatedSpecimen - lentSpecimens;
	}

	public List<Specimen> getSpecimens() {
		return specimens;
	}

	public Specimen getSpecimen(int index) {
		return this.specimens.get(index);
	}

	public void setSpecimens(List<Specimen> specimens) {
		this.specimens = specimens;
	}
	
	public Calendar getEditionDate() {
		return editionDate;
	}

	public void setEditionDate(Calendar editionDate) {
		this.editionDate = editionDate;
	}

	public int getNbCreatedSpecimen() {
		return nbCreatedSpecimen;
	}

	public void setNbCreatedSpecimen(int nbCreatedSpecimen) {
		this.nbCreatedSpecimen = nbCreatedSpecimen;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public void incrementCreatedSpecimens(){
		this.nbCreatedSpecimen++;
	}

	public void decrementCreatedSpecimens(){
		this.nbCreatedSpecimen--;
	}

	public Integer getLentSpecimens() {
		return lentSpecimens;
	}

	public void setLentSpecimens(Integer lentSpecimens) {
		this.lentSpecimens = lentSpecimens;
	}

	public List<Booking> getBookings() {
		return bookings;
	}

	public void setBookings(List<Booking> bookings) {
		this.bookings = bookings;
	}

	public int getBookedSpecimens() {
		return bookedSpecimens;
	}

	public void setBookedSpecimens(int bookedSpecimens) {
		this.bookedSpecimens = bookedSpecimens;
	}

	public void setLentSpecimens(int lentSpecimens) {
		this.lentSpecimens = lentSpecimens;
	}

	/** Constructeur **/

	public Media(){};

	public Media(String title, Calendar editionDate,
			String type, String isbnOrIsan, int nbSpecimen) throws BadParametersException, MediaExistsException{

		if ((title == null)  || (editionDate == null) || (type == null))
			throw new BadParametersException();

		this.setTitle(title);
		this.setEditionDate(editionDate);
		this.setType(type);
		this.setIsbnOrIsan(isbnOrIsan);
		
		this.specimens = new ArrayList<Specimen>();
		this.bookings = new ArrayList<Booking>();
		this.authors = new ArrayList<Author>();

		for (int i = 0; i < nbSpecimen; i++){
			Specimen spec = new Specimen(this);
			this.addSpecimen(spec);
		}

		this.lentSpecimens = 0;
	}

	/**
	 * @throws BadParametersException 
	 * @throws MediaExistsException 
	 */
	public void addSpecimen(Specimen specimen) throws BadParametersException, MediaExistsException{
		if (specimen == null)
			throw new BadParametersException();
		if (IsASpecimen(specimen))
			throw new MediaExistsException();

		this.specimens.add(specimen);
		this.incrementCreatedSpecimens();
	}
	
	/**
	 * Removes a specimen into dB
	 * @throws BadParametersException 
	 * @throws MediaExistsException 
	 */
	public void removeSpecimen(Specimen specimen) throws BadParametersException, MediaExistsException{
		if (specimen == null)
			throw new BadParametersException();

		this.specimens.remove(specimen);
		this.decrementCreatedSpecimens();
	}
	
	/**
	 * Adds a specimen into dB
	 * @throws BadParametersException
	 * @throws MediaExistsException
	 */
	public void addSpecimen() throws BadParametersException, MediaExistsException{
		Specimen specimen = new Specimen(this);
		this.specimens.add(specimen);
		this.incrementCreatedSpecimens();
	}

	/**
	 * Checks if the specimen exists in DB
	 * @param specimen
	 * @return
	 * @throws BadParametersException
	 */
	public boolean IsASpecimen(Specimen specimen) throws BadParametersException {
		if (specimen == null)
			throw new BadParametersException();

		// If the object is in the list,
		// it is a known specimen
		return specimens.contains(specimen);
	}
	
	/**
	 * 
	 */
	public void lendSpecimen() {
		this.setLentSpecimens(lentSpecimens + 1);
	}
	
	/**
	 * 
	 * @param spec
	 */
	public void returnSpecimen(Specimen spec) {
		this.setLentSpecimens(lentSpecimens - 1);

		/* Gestion des reservations :
		   Si des reservations existent
		   on assigne la plus ancienne a l'exemplaire retourne
		*/
		if(bookings.size() > 0) {
			Collections.sort(bookings);
			Booking booking = this.bookings.get(0);

			booking.setSpecimen(spec);
			booking.startBooking();

			this.bookings.remove(booking);
			this.bookedSpecimens--;
		}
	}

	/**
	 * Checks if the media is bookable
	 * @return
	 */
	public boolean isBookable(){
		if(this.bookedSpecimens == this.nbCreatedSpecimen)
			return false;
		else
			return true;		
	}
	
	/**
	 * Adds a new reservation
	 * @param booking
	 */
	public void addBooking(Booking booking) {
		this.bookings.add(booking);
		this.bookedSpecimens++;
	}

	/**
	 * Returns a description of the media
	 */
	public String toString() {
		return this.title;
	}

	public void addAuthor(Author author) {
		this.authors.add(author);
	}
}
